using SqlSugar;
using Digitalmes.Domain.Repositories;
using Digitalmes.Infrastructure.Unique;

namespace Digitalmes.Infrastructure.Data;

/// <summary>
/// SqlSugar 设置。
/// </summary>
internal static class SqlSugarSetup
{
    /// <summary>
    /// 注册基于 SqlSugar 配置的服务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="hostBuilderContext"></param>
    /// <param name="name">数据库连接的配置名称</param>
    public static void AddSqlSugarSetup(this IServiceCollection services, HostBuilderContext hostBuilderContext, string name = "DbConnection")
    {
        // 自定义 SqlSugar 雪花ID算法
        StaticConfig.CustomSnowFlakeFunc = IdGeneratorHelper.NextId;

        var dbOptions = hostBuilderContext.Configuration.GetSection(name).Get<DbConnectionOptions>();

        SqlSugarScope CreateSqlSugarFunc(IServiceProvider sp) => new(
            BuildDbConfigs(sp, dbOptions?.ConnectionConfigs!, hostBuilderContext.HostingEnvironment),
            sqlSugarClient => dbOptions?.ConnectionConfigs.ForEach(config =>
            {
                var dbScopeProvider = sqlSugarClient.GetConnectionScope(config.ConfigId);
                SetDbAop(sp, dbScopeProvider, hostBuilderContext.HostingEnvironment);
            }));

        services.AddSingleton<ISqlSugarClient>((Func<IServiceProvider, SqlSugarScope>)CreateSqlSugarFunc); // 单例注册
        services.AddScoped(typeof(IRepository<>), typeof(SqlSugarRepository<>)); // 仓储注册
        services.AddSingleton<ICacheService, SqlSugarMemoryCache>(); // 二级缓存，根据实际进行缓存切换

        // 注册工作单元。注意，工作单元数据不能跨 DB。
        services.AddTransient<IUnitOfWorkManage, UnitOfWorkManager>();

        // 本地函数
        List<ConnectionConfig> BuildDbConfigs(IServiceProvider sp, List<DbConnectionConfig> configs, IHostEnvironment env)
        {
            configs.ForEach(config =>
            {
                SetDbConfig(sp, config, hostBuilderContext.HostingEnvironment);
            });

            return [.. configs.OfType<ConnectionConfig>()];
        }
    }

    /// <summary>
    /// 配置连接属性
    /// </summary>
    /// <param name="config"></param>
    public static void SetDbConfig(IServiceProvider sp, DbConnectionConfig config, IHostEnvironment env)
    {
        // 若 ConfigId 没有设置，会使用默认数据库标识。
        // 注：配置连接中的第一个不需要配置，其他的必须配置不同值。
        config.ConfigureExternalServices = new ConfigureExternalServices
        {
            EntityNameService = (type, entity) => // 处理表
            {
                entity.IsDisabledDelete = true; // 禁止删除非 sqlsugar 创建的列
                // 只处理设置了[SugarTable]特性的表
                if (!type.IsDefined(typeof(SugarTable), false))
                {
                    return;
                }

                if (config.EnableUnderLine && !entity.DbTableName.Contains('_'))
                {
                    entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName); // 驼峰转下划线
                }

                // 使用自定义的 Schema
                if (!string.IsNullOrWhiteSpace(config.Schema))
                {
                    entity.DbTableName = $"{config.Schema}.{entity.DbTableName}";
                }
            },
            EntityService = (type, column) => // 处理列
            {
                // 只处理设置了[SugarColumn]特性的列
                if (!type.IsDefined(typeof(SugarColumn), false))
                {
                    return;
                }

                if (new NullabilityInfoContext().Create(type).WriteState is NullabilityState.Nullable)
                {
                    column.IsNullable = true;
                }

                if (config.EnableUnderLine && !column.IsIgnore && !column.DbColumnName.Contains('_'))
                {
                    column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName); // 驼峰转下划线
                }
            },
            DataInfoCacheService = sp.GetService<ICacheService>(), // 设置二级缓存
        };

        config.InitKeyType = InitKeyType.Attribute;
        config.IsAutoCloseConnection = true;
        config.MoreSettings = new ConnMoreSettings
        {
            IsAutoRemoveDataCache = true, // 所有增、删和改操作会自动调用 RemoveDataCache()，
                                          // 其中基本单表和联表查询（包括导航属性）都支持，若是子查询或者Mergetable需采用追加key模式手动删除
            IsAutoDeleteQueryFilter = true, // 启用删除查询过滤器
            IsAutoUpdateQueryFilter = true, // 启用更新查询过滤器
            SqlServerCodeFirstNvarchar = true, // 采用Nvarchar
        };

        // 若库类型是人大金仓则默认设置PG模式
        if (config.DbType == DbType.Kdbndp)
            config.MoreSettings.DatabaseModel = DbType.PostgreSQL; // 配置PG模式主要是兼容系统表差异

        // 若库类型是Oracle则默认主键名字和参数名字最大长度
        if (config.DbType == DbType.Oracle)
            config.MoreSettings.MaxParameterNameLength = 30;
    }

    /// <summary>
    /// 配置Aop
    /// </summary>
    /// <param name="db"></param>
    public static void SetDbAop(IServiceProvider sp, SqlSugarScopeProvider db, IHostEnvironment env)
    {
        var config = db.CurrentConnectionConfig;

        // 设置超时时间
        db.Ado.CommandTimeOut = 30;

        // 打印SQL语句
        db.Aop.OnLogExecuting = (sql, pars) =>
        {
            // 仅开发环境显示
            if (env.IsDevelopment())
            {
                var dateTime = sp.GetRequiredService<TimeProvider>().GetLocalNow();

                var originColor = Console.ForegroundColor;
                if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                }
                else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                }
                else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                }

                var sqlStr = UtilMethods.GetSqlString(config.DbType, sql, pars);
                Console.WriteLine($"[{dateTime:yyyy-MM-dd HH:mm:ss.fff} —— SQL Executing] {Environment.NewLine}{sqlStr}{Environment.NewLine}");
                Console.ForegroundColor = originColor;
            }
        };

        db.Aop.OnError = ex =>
        {
            if (ex.Parametres == null)
            {
                return;
            }

            var dateTime = sp.GetRequiredService<TimeProvider>().GetLocalNow();

            var originColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.DarkRed;
            var sqlStr = UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres);
            Console.WriteLine($"[{dateTime:yyyy-MM-dd HH:mm:ss.fff} —— SQL Error] {Environment.NewLine}{sqlStr}{Environment.NewLine}");
            Console.ForegroundColor = originColor;
        };

        // 数据审计
        db.Aop.DataExecuting = (oldValue, entityInfo) =>
        {
            if (entityInfo.OperationType == DataFilterType.InsertByObject)
            {
                // insert 中设置主键。 
                // 非自增主键(long类型)且没有值的---赋值雪花Id
                if (entityInfo.EntityColumnInfo.IsPrimarykey
                    && !entityInfo.EntityColumnInfo.IsIdentity
                    && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
                {
                    var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                    if (id == null || (long)id == 0)
                    {
                        entityInfo.SetValue(SnowFlakeSingle.Instance.NextId());
                    }
                }

                // insert 设置 CreatedBy/CreatedTime 和 UpdatedBy/UpdatedTime
                SetInsertAuditingFn(entityInfo);
                SetUpdateAuditingFn(entityInfo);
            }
            else if (entityInfo.OperationType == DataFilterType.UpdateByObject)
            {
                // update 设置 UpdatedBy/UpdatedTime
                SetUpdateAuditingFn(entityInfo);
            }
        };

        void SetInsertAuditingFn(DataFilterModel entityInfo)
        {
            if (entityInfo.EntityValue is ICreationAuditedEntity)
            {
                if (entityInfo.PropertyName == nameof(ICreationAuditedEntity.CreatedTime))
                {
                    var dateTime = sp.GetRequiredService<TimeProvider>().GetLocalNow();
                    entityInfo.SetValue(dateTime.DateTime);
                }
                else if (entityInfo.PropertyName == nameof(ICreationAuditedEntity.CreatedBy))
                {
                    var userName = sp.GetKeyedService<ICurrentUser>(typeof(ICurrentUser))?.Username;
                    if (!string.IsNullOrEmpty(userName))
                    {
                        entityInfo.SetValue(userName);
                    }
                }
            }
        }

        void SetUpdateAuditingFn(DataFilterModel entityInfo)
        {
            if (entityInfo.EntityValue is IUpdationAuditedEntity)
            {
                if (entityInfo.PropertyName == nameof(IUpdationAuditedEntity.UpdatedTime))
                {
                    var dateTime = sp.GetRequiredService<TimeProvider>().GetLocalNow();
                    entityInfo.SetValue(dateTime.DateTime);
                }
                else if (entityInfo.PropertyName == nameof(IUpdationAuditedEntity.UpdatedBy))
                {
                    var userName = sp.GetKeyedService<ICurrentUser>(typeof(ICurrentUser))?.Username;
                    if (!string.IsNullOrEmpty(userName))
                    {
                        entityInfo.SetValue(userName);
                    }
                }
            }
        }

        // 配置实体假删除过滤器
        db.QueryFilter.AddTableFilter<ISoftDelete>(u => u.IsDeleted == false);
    }
}
